// 验证码图片
+function($) {
    "use strict";

    var CaptchaImage = function(element, options) {
        this.options = options;
        this.$element = $(element);
        this.$captchaId = null;
        this.captchaId = uuid();

        this.init();
    };

    CaptchaImage.prototype.init = function() {
        this.refresh();
        this.$captchaId = $('<input name="captchaId" type="hidden" value="' + this.captchaId + '">').insertAfter(this.$element);
        this.$element.on("click.shopxx.captchaImage", $.proxy(this.refresh, this));
    };

    CaptchaImage.prototype.generateSrc = function() {
        return "/common/captcha/image?captchaId=" + this.captchaId + "&timestamp=" + new Date().getTime();
    };

    CaptchaImage.prototype.refresh = function() {
        var refreshEvent = $.Event("refresh.shopxx.captchaImage");

        this.$element.trigger(refreshEvent);
        if (refreshEvent.isDefaultPrevented()) {
            return;
        }

        this.$element.attr("src", this.generateSrc());
        this.$element.trigger("refreshed.shopxx.captchaImage");
    };

    function uuid() {
        var uuidChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");
        var r;
        var uuid = [];
        uuid[8] = uuid[13] = uuid[18] = uuid[23] = "-";
        uuid[14] = "4";

        for (var i = 0; i < 36; i++) {
            if (!uuid[i]) {
                r = 0 | Math.random() * 16;
                uuid[i] = uuidChars[(i == 19) ? (r & 0x3) | 0x8 : r];
            }
        }
        return uuid.join("");
    }

    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("shopxx.captchaImage");
            var options = $.extend({}, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("shopxx.captchaImage", (data = new CaptchaImage(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.captchaImage;

    $.fn.captchaImage = Plugin;
    $.fn.captchaImage.Constructor = CaptchaImage;

    $.fn.captchaImage.noConflict = function() {
        $.fn.captchaImage = old;
        return this;
    };

    $(window).on("load", function() {
        $("[data-toggle='captchaImage']").each(function() {
            var $this = $(this);

            Plugin.call($this);
        });
    });

}(jQuery);

// 滚动加载
+function($) {
    "use strict";

    var ScrollLoad = function(element, options) {
        this.options = options;
        this.$element = $(element);
        this.scrollload = null;
        this.pageNumber = 1;

        this.init();
    };

    ScrollLoad.DEFAULTS = {
        url: null,
        type: "GET",
        data: null,
        dataType: "json",
        contentTarget: "#scrollLoadContent",
        templateTarget: "#scrollLoadTemplate"
    };

    ScrollLoad.prototype.init = function() {
        var contentTarget = typeof this.options.contentTarget === "function" ? this.options.contentTarget(this.$element) : this.options.contentTarget;
        var $contentTarget = contentTarget instanceof jQuery ? contentTarget : $(contentTarget);

        if (typeof Scrollload === "undefined") {
            throw new Error("ScrollLoad requires scrollload.js");
        }

        this.scrollload = new Scrollload({
            container: this.$element[0],
            content: $contentTarget[0],
            loadMore: $.proxy(this.load, this)
        });
    };

    ScrollLoad.prototype.load = function() {
        var that = this;
        var url = typeof this.options.url === "function" ? this.options.url(this.$element) : this.options.url;
        var type = typeof this.options.type === "function" ? this.options.type(this.$element) : this.options.type;
        var data = typeof this.options.data === "function" ? this.options.data(this.$element) : this.options.data;
        var dataType = typeof this.options.dataType === "function" ? this.options.dataType(this.$element) : this.options.dataType;
        var templateTarget = typeof this.options.templateTarget === "function" ? this.options.templateTarget(this.$element) : this.options.templateTarget;
        var $templateTarget = templateTarget instanceof jQuery ? templateTarget : $(templateTarget);
        var loadEvent = $.Event("load.shopxx.scrollLoad");

        if (typeof _ === "undefined") {
            throw new Error("ScrollLoad requires underscore.js");
        }

        this.$element.trigger(loadEvent);
        if (loadEvent.isDefaultPrevented()) {
            return;
        }

        $.ajax({
            url: _.template(url)({
                pageNumber: this.pageNumber++
            }),
            type: type,
            data: data,
            dataType: dataType
        }).done(function(data) {
            if (data != null && data.length > 0) {
                var template = _.template($templateTarget.html());

                $(that.scrollload.contentDom).append(template({
                    data: data
                }));
                that.scrollload.unLock();
            } else {
                that.scrollload.noMoreData();
            }
            that.$element.trigger("loaded.shopxx.scrollLoad", arguments);
        }).fail(function() {
            that.scrollload.throwException();
        });
    };

    ScrollLoad.prototype.refresh = function() {
        this.pageNumber = 1;
        $(this.scrollload.contentDom).empty();
        this.scrollload.refreshData();
    };

    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("shopxx.scrollLoad");
            var options = $.extend({}, ScrollLoad.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("shopxx.scrollLoad", (data = new ScrollLoad(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.scrollLoad;

    $.fn.scrollLoad = Plugin;
    $.fn.scrollLoad.Constructor = ScrollLoad;

    $.fn.scrollLoad.noConflict = function() {
        $.fn.scrollLoad = old;
        return this;
    };

    $(window).on("load", function() {
        $("[data-spy='scrollLoad']").each(function() {
            var $this = $(this);

            Plugin.call($this);
        });
    });

}(jQuery);

// 加入购物车
+function($) {
    "use strict";

    var AddCart = function(element, options) {
        this.options = options;
        this.$element = $(element);
        this.$document = $(document);

        this.$element.on("click.shopxx.addCart", $.proxy(this.execute, this));
    };

    AddCart.DEFAULTS = {
        skuId: null,
        quantity: 1,
        cartTarget: null,
        productImageTarget: null
    };

    AddCart.prototype.ajax = function() {
        var skuId = typeof this.options.skuId === "function" ? this.options.skuId(this.$element) : this.options.skuId;
        var quantity = typeof this.options.quantity === "function" ? this.options.quantity(this.$element) : this.options.quantity;

        return $.addCart(skuId, quantity);
    };

    AddCart.prototype.fly = function(callback) {
        var that = this;
        var cartTarget = typeof this.options.cartTarget === "function" ? this.options.cartTarget(this.$element) : this.options.cartTarget;
        var productImageTarget = typeof this.options.productImageTarget === "function" ? this.options.productImageTarget(this.$element) : this.options.productImageTarget;
        var $cartTarget = cartTarget instanceof jQuery ? cartTarget : $(cartTarget);
        var $productImageTarget = productImageTarget instanceof jQuery ? productImageTarget : $(productImageTarget);

        if ($.fn.velocity == null) {
            throw new Error("AddCart requires velocity.js");
        }
        if ($.fn.fly == null) {
            throw new Error("AddCart requires jquery.fly.js");
        }

        if ($cartTarget != null && $cartTarget.length > 0 && $productImageTarget != null && $productImageTarget.length > 0) {
            $productImageTarget.clone().css({
                width: 50,
                height: 50,
                position: "absolute",
                top: $productImageTarget.offset().top + $productImageTarget.outerHeight() / 2 - 25,
                left: $productImageTarget.offset().left + $productImageTarget.outerWidth() / 2 - 25,
                "z-index": 300,
                opacity: 0,
                "border-radius": "50%"
            }).appendTo("body").velocity({
                opacity: 0.6
            }, {
                complete: function() {
                    var $element = $(this);

                    $element.fly({
                        start: {
                            top: $element.offset().top - that.$document.scrollTop(),
                            left: $element.offset().left
                        },
                        end: {
                            width: 10,
                            height: 10,
                            top: $cartTarget.offset().top - that.$document.scrollTop() + $cartTarget.outerHeight() / 2 - 5,
                            left: $cartTarget.offset().left + $cartTarget.outerWidth() / 2 - 5
                        },
                        onEnd: function() {
                            this.destroy();
                            callback.call(that);
                        }
                    });
                }
            });
        } else {
            callback.call(that);
        }
    };

    AddCart.prototype.execute = function() {
        var that = this;
        var beforeEvent = $.Event("before.shopxx.addCart");

        this.$element.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        this.ajax().done(function() {
            var ajaxArguments = arguments;

            that.fly(function() {
                that.$element.trigger("success.shopxx.addCart", ajaxArguments);
                that.$element.trigger("complete.shopxx.addCart", ajaxArguments);
            });
        }).fail(function() {
            that.$element.trigger("error.shopxx.addCart", arguments);
            that.$element.trigger("complete.shopxx.addCart", arguments);
        });
    };

    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("shopxx.addCart");
            var options = $.extend({}, AddCart.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("shopxx.addCart", (data = new AddCart(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.addCart;

    $.fn.addCart = Plugin;
    $.fn.addCart.Constructor = AddCart;

    $.fn.addCart.noConflict = function() {
        $.fn.addCart = old;
        return this;
    };

    $(document).on("click.shopxx.addCart.data-api", "[data-action='addCart']", function() {
        var $this = $(this);

        if ($this.data("shopxx.addCart") != null) {
            return;
        }
        Plugin.call($this, "execute");
    });

}(jQuery);

// 修改购物车
+function($) {
    "use strict";

    var ModifyCart = function(element, options) {
        this.options = options;
        this.$element = $(element);

        if ($.fn.spinner == null) {
            throw new Error("ModifyCart requires jquery.spinner.js");
        }

        this.$element.on("changing.spinner", $.proxy(this.changing, this));
    };

    ModifyCart.DEFAULTS = {
        skuId: null
    };

    ModifyCart.prototype.ajax = function(newValue) {
        var skuId = typeof this.options.skuId === "function" ? this.options.skuId(this.$element) : this.options.skuId;

        return $.modifyCart(skuId, newValue);
    };

    ModifyCart.prototype.changing = function(event, newValue, oldValue) {
        var that = this;
        var beforeEvent = $.Event("before.shopxx.modifyCart");

        this.$element.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        this.ajax(newValue).done(function() {
            that.$element.trigger("success.shopxx.modifyCart", arguments);
        }).fail(function() {
            that.$element.val(oldValue);
            that.$element.trigger("error.shopxx.modifyCart", arguments);
        }).always(function() {
            that.$element.trigger("complete.shopxx.modifyCart", arguments);
        });
    };

    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("shopxx.modifyCart");
            var options = $.extend({}, ModifyCart.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("shopxx.modifyCart", (data = new ModifyCart(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.modifyCart;

    $.fn.modifyCart = Plugin;
    $.fn.modifyCart.Constructor = ModifyCart;

    $.fn.modifyCart.noConflict = function() {
        $.fn.modifyCart = old;
        return this;
    };

    $(window).on("load", function() {
        $("[data-action='modifyCart']").each(function() {
            var $this = $(this);

            Plugin.call($this);
        });
    });

}(jQuery);

// 移除购物车
+function($) {
    "use strict";

    var RemoveCart = function(element, options) {
        this.options = options;
        this.$element = $(element);

        this.$element.on("click.shopxx.removeCart", $.proxy(this.execute, this));
    };

    RemoveCart.DEFAULTS = {
        skuId: null,
        confirm: true,
        confirmMessage: "您确定要移除该商品吗？"
    };

    RemoveCart.prototype.confirm = function(callback) {
        var confirmMessage = typeof this.options.confirmMessage === "function" ? this.options.confirmMessage(this.$element) : this.options.confirmMessage;

        if (typeof bootbox === "undefined") {
            throw new Error("RemoveCart requires bootbox.js");
        }

        bootbox.confirm(confirmMessage, $.proxy(callback, this));
    };

    RemoveCart.prototype.ajax = function() {
        var skuId = typeof this.options.skuId === "function" ? this.options.skuId(this.$element) : this.options.skuId;

        return $.removeCart(skuId);
    };

    RemoveCart.prototype.execute = function() {
        var that = this;
        var confirm = typeof this.options.confirm === "function" ? this.options.confirm(this.$element) : this.options.confirm;
        var beforeEvent = $.Event("before.shopxx.removeCart");

        this.$element.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        if (confirm) {
            this.confirm(function(result) {
                if (result == null || !result) {
                    return;
                }

                that.ajax().done(function() {
                    that.$element.trigger("success.shopxx.removeCart", arguments);
                }).fail(function() {
                    that.$element.trigger("error.shopxx.removeCart", arguments);
                }).always(function() {
                    that.$element.trigger("complete.shopxx.removeCart", arguments);
                });
            });
        } else {
            this.ajax().done(function() {
                that.$element.trigger("success.shopxx.removeCart", arguments);
            }).fail(function() {
                that.$element.trigger("error.shopxx.removeCart", arguments);
            }).always(function() {
                that.$element.trigger("complete.shopxx.removeCart", arguments);
            });
        }
    };

    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("shopxx.removeCart");
            var options = $.extend({}, RemoveCart.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("shopxx.removeCart", (data = new RemoveCart(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.removeCart;

    $.fn.removeCart = Plugin;
    $.fn.removeCart.Constructor = RemoveCart;

    $.fn.removeCart.noConflict = function() {
        $.fn.removeCart = old;
        return this;
    };

    $(document).on("click.shopxx.removeCart.data-api", "[data-action='removeCart']", function() {
        var $this = $(this);

        if ($this.data("shopxx.removeCart") != null) {
            return;
        }
        Plugin.call($this, "execute");
    });

}(jQuery);

// 清空购物车
+function($) {
    "use strict";

    var ClearCart = function(element, options) {
        this.options = options;
        this.$element = $(element);

        this.$element.on("click.shopxx.clearCart", $.proxy(this.execute, this));
    };

    ClearCart.DEFAULTS = {
        confirm: true,
        confirmMessage: "您确定要清空购物车吗？"
    };

    ClearCart.prototype.confirm = function(callback) {
        var confirmMessage = typeof this.options.confirmMessage === "function" ? this.options.confirmMessage(this.$element) : this.options.confirmMessage;

        if (typeof bootbox === "undefined") {
            throw new Error("ClearCart requires bootbox.js");
        }

        bootbox.confirm(confirmMessage, $.proxy(callback, this));
    };

    ClearCart.prototype.ajax = function() {
        return $.clearCart();
    };

    ClearCart.prototype.execute = function() {
        var that = this;
        var confirm = typeof this.options.confirm === "function" ? this.options.confirm(this.$element) : this.options.confirm;
        var beforeEvent = $.Event("before.shopxx.clearCart");

        this.$element.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        if (confirm) {
            this.confirm(function(result) {
                if (result == null || !result) {
                    return;
                }

                that.ajax().done(function() {
                    that.$element.trigger("success.shopxx.clearCart", arguments);
                }).fail(function() {
                    that.$element.trigger("error.shopxx.clearCart", arguments);
                }).always(function() {
                    that.$element.trigger("complete.shopxx.clearCart", arguments);
                });
            });
        } else {
            this.ajax().done(function() {
                that.$element.trigger("success.shopxx.clearCart", arguments);
            }).fail(function() {
                that.$element.trigger("error.shopxx.clearCart", arguments);
            }).always(function() {
                that.$element.trigger("complete.shopxx.clearCart", arguments);
            });
        }
    };

    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("shopxx.clearCart");
            var options = $.extend({}, ClearCart.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("shopxx.clearCart", (data = new ClearCart(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.clearCart;

    $.fn.clearCart = Plugin;
    $.fn.clearCart.Constructor = ClearCart;

    $.fn.clearCart.noConflict = function() {
        $.fn.clearCart = old;
        return this;
    };

    $(document).on("click.shopxx.clearCart.data-api", "[data-action='clearCart']", function() {
        var $this = $(this);

        if ($this.data("shopxx.clearCart") != null) {
            return;
        }
        Plugin.call($this, "execute");
    });

}(jQuery);

// 结算
+function($) {
    "use strict";

    var Checkout = function(element, options) {
        this.options = options;
        this.$element = $(element);

        this.$element.on("click.shopxx.checkout", $.proxy(this.execute, this));
    };

    Checkout.DEFAULTS = {
        skuId: null,
        quantity: 1
    };

    Checkout.prototype.execute = function() {
        var that = this;
        var skuId = typeof this.options.skuId === "function" ? this.options.skuId(this.$element) : this.options.skuId;
        var quantity = typeof this.options.quantity === "function" ? this.options.quantity(this.$element) : this.options.quantity;
        var beforeEvent = $.Event("before.shopxx.checkout");

        this.$element.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        $.checkout(skuId, quantity);
    };

    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("shopxx.checkout");
            var options = $.extend({}, Checkout.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("shopxx.checkout", (data = new Checkout(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.checkout;

    $.fn.checkout = Plugin;
    $.fn.checkout.Constructor = Checkout;

    $.fn.checkout.noConflict = function() {
        $.fn.checkout = old;
        return this;
    };

    $(document).on("click.shopxx.checkout.data-api", "[data-action='checkout']", function() {
        var $this = $(this);

        if ($this.data("shopxx.checkout") != null) {
            return;
        }
        Plugin.call($this, "execute");
    });

}(jQuery);

// 添加商品收藏
+function($) {
    "use strict";

    var AddProductFavorite = function(element, options) {
        this.options = options;
        this.$element = $(element);

        this.$element.on("click.shopxx.addProductFavorite", $.proxy(this.execute, this));
    };

    AddProductFavorite.DEFAULTS = {
        productId: null
    };

    AddProductFavorite.prototype.ajax = function() {
        var productId = typeof this.options.productId === "function" ? this.options.productId(this.$element) : this.options.productId;

        return $.addProductFavorite(productId);
    };

    AddProductFavorite.prototype.shrinkOut = function(callback) {
        var that = this;

        if ($.fn.velocity == null) {
            throw new Error("AddProductFavorite requires velocity.js");
        }

        $('<i class="iconfont icon-likefill"></i>').css({
            "line-height": "30px",
            color: "#ff6900",
            "font-size": "30px",
            position: "absolute",
            top: this.$element.offset().top + this.$element.outerHeight() / 2 - 15,
            left: this.$element.offset().left + this.$element.outerWidth() / 2 - 15,
            "z-index": 300
        }).appendTo("body").velocity("transition.shrinkOut", {
            complete: function() {
                $(this).remove();
                callback.call(that);
            }
        });
    };

    AddProductFavorite.prototype.execute = function() {
        var that = this;
        var beforeEvent = $.Event("before.shopxx.addProductFavorite");

        this.$element.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        this.ajax().done(function() {
            var ajaxArguments = arguments;

            that.shrinkOut(function() {
                that.$element.trigger("success.shopxx.addProductFavorite", ajaxArguments);
                that.$element.trigger("complete.shopxx.addProductFavorite", ajaxArguments);
            });
        }).fail(function() {
            that.$element.trigger("error.shopxx.addProductFavorite", arguments);
            that.$element.trigger("complete.shopxx.addProductFavorite", arguments);
        });
    };

    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("shopxx.addProductFavorite");
            var options = $.extend({}, AddProductFavorite.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("shopxx.addProductFavorite", (data = new AddProductFavorite(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.addProductFavorite;

    $.fn.addProductFavorite = Plugin;
    $.fn.addProductFavorite.Constructor = AddProductFavorite;

    $.fn.addProductFavorite.noConflict = function() {
        $.fn.addProductFavorite = old;
        return this;
    };

    $(document).on("click.shopxx.addProductFavorite.data-api", "[data-action='addProductFavorite']", function() {
        var $this = $(this);

        if ($this.data("shopxx.addProductFavorite") != null) {
            return;
        }
        Plugin.call($this, "execute");
    });

}(jQuery);

// 添加店铺收藏
+function($) {
    "use strict";

    var AddStoreFavorite = function(element, options) {
        this.options = options;
        this.$element = $(element);

        this.$element.on("click.shopxx.addStoreFavorite", $.proxy(this.execute, this));
    };

    AddStoreFavorite.DEFAULTS = {
        storeId: null
    };

    AddStoreFavorite.prototype.ajax = function() {
        var storeId = typeof this.options.storeId === "function" ? this.options.storeId(this.$element) : this.options.storeId;

        return $.addStoreFavorite(storeId);
    };

    AddStoreFavorite.prototype.shrinkOut = function(callback) {
        var that = this;

        if ($.fn.velocity == null) {
            throw new Error("AddStoreFavorite requires velocity.js");
        }

        $('<i class="iconfont icon-likefill"></i>').css({
            "line-height": "30px",
            color: "#ff6900",
            "font-size": "30px",
            position: "absolute",
            top: this.$element.offset().top + this.$element.outerHeight() / 2 - 15,
            left: this.$element.offset().left + this.$element.outerWidth() / 2 - 15,
            "z-index": 300
        }).appendTo("body").velocity("transition.shrinkOut", {
            complete: function() {
                $(this).remove();
                callback.call(that);
            }
        });
    };

    AddStoreFavorite.prototype.execute = function() {
        var that = this;
        var beforeEvent = $.Event("before.shopxx.addStoreFavorite");

        this.$element.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        this.ajax().done(function() {
            var ajaxArguments = arguments;

            that.shrinkOut(function() {
                that.$element.trigger("success.shopxx.addStoreFavorite", ajaxArguments);
                that.$element.trigger("complete.shopxx.addStoreFavorite", ajaxArguments);
            });
        }).fail(function() {
            that.$element.trigger("error.shopxx.addStoreFavorite", arguments);
            that.$element.trigger("complete.shopxx.addStoreFavorite", arguments);
        });
    };

    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("shopxx.addStoreFavorite");
            var options = $.extend({}, AddStoreFavorite.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("shopxx.addStoreFavorite", (data = new AddStoreFavorite(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.addStoreFavorite;

    $.fn.addStoreFavorite = Plugin;
    $.fn.addStoreFavorite.Constructor = AddStoreFavorite;

    $.fn.addStoreFavorite.noConflict = function() {
        $.fn.addStoreFavorite = old;
        return this;
    };

    $(document).on("click.shopxx.addStoreFavorite.data-api", "[data-action='addStoreFavorite']", function() {
        var $this = $(this);

        if ($this.data("shopxx.addStoreFavorite") != null) {
            return;
        }
        Plugin.call($this, "execute");
    });

}(jQuery);

// 删除
+function($) {
    "use strict";

    var Delete = function(element, options) {
        this.options = options;
        this.$element = $(element);

        this.$element.on("click.shopxx.delete", $.proxy(this.execute, this));
    };

    Delete.DEFAULTS = {
        url: "delete",
        type: "POST",
        data: null,
        dataType: "json",
        confirm: true,
        confirmMessage: "您确定要删除吗？"
    };

    Delete.prototype.confirm = function(callback) {
        var confirmMessage = typeof this.options.confirmMessage === "function" ? this.options.confirmMessage(this.$element) : this.options.confirmMessage;

        if (typeof bootbox === "undefined") {
            throw new Error("Delete requires bootbox.js");
        }

        bootbox.confirm(confirmMessage, $.proxy(callback, this));
    };

    Delete.prototype.ajax = function() {
        var url = typeof this.options.url === "function" ? this.options.url(this.$element) : this.options.url;
        var type = typeof this.options.type === "function" ? this.options.type(this.$element) : this.options.type;
        var data = typeof this.options.data === "function" ? this.options.data(this.$element) : this.options.data;
        var dataType = typeof this.options.dataType === "function" ? this.options.dataType(this.$element) : this.options.dataType;

        return $.ajax({
            url: url,
            type: type,
            data: data,
            dataType: dataType
        });
    };

    Delete.prototype.execute = function() {
        var that = this;
        var confirm = typeof this.options.confirm === "function" ? this.options.confirm(this.$element) : this.options.confirm;
        var beforeEvent = $.Event("before.shopxx.delete");

        this.$element.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        if (confirm) {
            this.confirm(function(result) {
                if (result == null || !result) {
                    return;
                }

                that.ajax().done(function() {
                    that.$element.trigger("success.shopxx.delete", arguments);
                }).fail(function() {
                    that.$element.trigger("error.shopxx.delete", arguments);
                }).always(function() {
                    that.$element.trigger("complete.shopxx.delete", arguments);
                });
            });
        } else {
            this.ajax().done(function() {
                that.$element.trigger("success.shopxx.delete", arguments);
            }).fail(function() {
                that.$element.trigger("error.shopxx.delete", arguments);
            }).always(function() {
                that.$element.trigger("complete.shopxx.delete", arguments);
            });
        }
    };

    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("shopxx.delete");
            var options = $.extend({}, Delete.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("shopxx.delete", (data = new Delete(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn["delete"];

    $.fn["delete"] = Plugin;
    $.fn["delete"].Constructor = Delete;

    $.fn["delete"].noConflict = function() {
        $.fn["delete"] = old;
        return this;
    };

    $(document).on("click.shopxx.delete.data-api", "[data-action='delete']", function() {
        var $this = $(this);

        if ($this.data("shopxx.delete") != null) {
            return;
        }
        Plugin.call($this, "execute");
    });

}(jQuery);

// 刷新
+function($) {
    "use strict";

    var Refresh = function(element, options) {
        this.options = options;
        this.$element = $(element);

        this.$element.on("click.shopxx.refresh", $.proxy(this.execute, this));
    };

    Refresh.DEFAULTS = {
        force: true
    };

    Refresh.prototype.execute = function() {
        var force = typeof this.options.force === "function" ? this.options.force(this.$element) : this.options.force;
        var beforeEvent = $.Event("before.shopxx.refresh");

        this.$element.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        location.reload(force);

        this.$element.trigger("complete.shopxx.refresh");
    };

    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("shopxx.refresh");
            var options = $.extend({}, Refresh.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("shopxx.refresh", (data = new Refresh(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.refresh;

    $.fn.refresh = Plugin;
    $.fn.refresh.Constructor = Refresh;

    $.fn.refresh.noConflict = function() {
        $.fn.refresh = old;
        return this;
    };

    $(document).on("click.shopxx.refresh.data-api", "[data-action='refresh']", function() {
        var $this = $(this);

        if ($this.data("shopxx.refresh") != null) {
            return;
        }
        Plugin.call($this, "execute");
    });

}(jQuery);

// 全选
+function($) {
    "use strict";

    var CheckAll = function(element, options) {
        this.options = options;
        this.$element = $(element);

        this.$element.on("change.shopxx.checkAll", $.proxy(this.toggle, this));
    };

    CheckAll.DEFAULTS = {
        target: "input[name='ids']"
    };

    CheckAll.prototype.toggle = function() {
        return this.$element.prop("checked") ? this.check() : this.uncheck();
    };

    CheckAll.prototype.check = function() {
        var target = typeof this.options.target === "function" ? this.options.target(this.$element) : this.options.target;
        var $target = target instanceof jQuery ? target : $(target);
        var checkEvent = $.Event("check.shopxx.checkAll");

        this.$element.trigger(checkEvent);
        if (checkEvent.isDefaultPrevented()) {
            return;
        }

        if (!this.$element.prop("checked")) {
            this.$element.prop("checked", true);
        }
        $target.filter(":not(:checked)").prop("checked", true).trigger("change");

        this.$element.trigger("checked.shopxx.checkAll");
    };

    CheckAll.prototype.uncheck = function() {
        var target = typeof this.options.target === "function" ? this.options.target(this.$element) : this.options.target;
        var $target = target instanceof jQuery ? target : $(target);
        var uncheckEvent = $.Event("uncheck.shopxx.checkAll");

        this.$element.trigger(uncheckEvent);
        if (uncheckEvent.isDefaultPrevented()) {
            return;
        }

        if (this.$element.prop("checked")) {
            this.$element.prop("checked", false);
        }
        $target.filter(":checked").prop("checked", false).trigger("change");

        this.$element.trigger("unchecked.shopxx.checkAll");
    };

    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("shopxx.checkAll");
            var options = $.extend({}, CheckAll.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("shopxx.checkAll", (data = new CheckAll(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.checkAll;

    $.fn.checkAll = Plugin;
    $.fn.checkAll.Constructor = CheckAll;

    $.fn.checkAll.noConflict = function() {
        $.fn.checkAll = old;
        return this;
    };

    $(document).on("change.shopxx.checkAll.data-api", "[data-toggle='checkAll']", function() {
        var $this = $(this);

        if ($this.data("shopxx.checkAll") != null) {
            return;
        }
        Plugin.call($this, "toggle");
    });

}(jQuery);

// 返回
+function($) {
    "use strict";

    var Back = function(element, options) {
        this.options = options;
        this.$element = $(element);

        this.$element.on("click.shopxx.back", $.proxy(this.execute, this));
    };

    Back.prototype.execute = function() {
        var beforeEvent = $.Event("before.shopxx.back");

        this.$element.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        history.back();

        this.$element.trigger("complete.shopxx.back");
    };

    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("shopxx.back");
            var options = $.extend({}, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("shopxx.back", (data = new Back(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.back;

    $.fn.back = Plugin;
    $.fn.back.Constructor = Back;

    $.fn.back.noConflict = function() {
        $.fn.back = old;
        return this;
    };

    $(document).on("click.shopxx.back.data-api", "[data-action='back']", function() {
        var $this = $(this);

        if ($this.data("shopxx.back") != null) {
            return;
        }
        Plugin.call($this, "execute");
    });

}(jQuery);

(function($) {

    var $document = $(document);
    var currentUserLocalStorageKey = "currentUser";
    var getCurrentCartUrl = "/cart/get_current";
    var addCartUrl = "/cart/add";
    var modifyCartUrl = "/cart/modify";
    var removeCartUrl = "/cart/remove";
    var clearCartUrl = "/cart/clear";
    var checkSkuUrl = "/order/check_sku";
    var checkCartUrl = "/order/check_cart";
    var checkoutUrl = "/order/checkout";
    var currentCartQuantitySessionStorageKey = "currentCartQuantity";
    var addProductFavoriteUrl = "/member/product_favorite/add";
    var addStoreFavoriteUrl = "/member/store_favorite/add";
    var spreadUserLocalStorageKey = "spreadUser";
    var redirectUrlPrefix = "/";
    var redirectUrlParameterName = "redirectUrl";
    var loginUrls = {
        member: "\/member/login",
        business: "\/business/login",
        admin: "\/admin/login"
    };

    // 货币格式化
    $.currency = function(value, showSign, showUnit) {
        if (value == null) {
            return "";
        }
        var result = (Math.round(value * Math.pow(10, 2)) / Math.pow(10, 2)).toFixed(2);
        if (showSign) {
            result = "￥" + result;
        }
        if (showUnit) {
            result += "元";
        }
        return result;
    };

    // 获取当前登录用户
    $.getCurrentUser = function() {
        return JSON.parse(localStorage.getItem(currentUserLocalStorageKey));
    };

    // 设置当前登录用户
    $.setCurrentUser = function(currentUser) {
        localStorage.setItem(currentUserLocalStorageKey, JSON.stringify(currentUser));
    };

    // 移除当前登录用户
    $.removeCurrentUser = function() {
        localStorage.removeItem(currentUserLocalStorageKey);
    };

    // 获取当前购物车
    // $.getCurrentCart = function() {
    //     return $.get(getCurrentCartUrl);
    // };

    // 加入购物车
    $.addCart = function(skuId, quantity) {
        return $.post(addCartUrl, {
            skuId: skuId,
            quantity: quantity
        });
    };

    // 修改购物车
    $.modifyCart = function(skuId, quantity) {
        return $.post(modifyCartUrl, {
            skuId: skuId,
            quantity: quantity
        });
    };

    // 移除购物车
    $.removeCart = function(skuId) {
        return $.post(removeCartUrl, {
            skuId: skuId
        });
    };

    // 清空购物车
    $.clearCart = function() {
        return $.post(clearCartUrl);
    };

    // // 获取当前购物车数量
    // $.getCurrentCartQuantity = function() {
    //     var currentCartQuantity = JSON.parse(sessionStorage.getItem(currentCartQuantitySessionStorageKey));
    //
    //     if (currentCartQuantity != null) {
    //         if (currentCartQuantity.expire > new Date().getTime()) {
    //             $.setCurrentCartQuantity(currentCartQuantity.value);
    //             return parseInt(currentCartQuantity.value);
    //         } else {
    //             $.removeCurrentCartQuantity();
    //         }
    //     }
    //     return null;
    // };

    // 设置当前购物车数量
    $.setCurrentCartQuantity = function(quantity) {
        sessionStorage.setItem(currentCartQuantitySessionStorageKey, JSON.stringify({
            value: quantity,
            expire: new Date().getTime() + 10 * 60 * 1000
        }));
    };

    // 移除当前购物车数量
    $.removeCurrentCartQuantity = function() {
        sessionStorage.removeItem(currentCartQuantitySessionStorageKey);
    };

    // 结算
    $.checkout = function(skuId, quantity) {
        if (skuId != null) {
            $.get(checkSkuUrl, {
                skuId: skuId,
                quantity: quantity
            }).done(function() {
                location.href = checkoutUrl + "?" + $.param({
                    skuId: skuId,
                    quantity: quantity
                });
            });
        } else {
            $.get(checkCartUrl).done(function() {
                location.href = checkoutUrl;
            });
        }
    };

    // 添加商品收藏
    $.addProductFavorite = function(productId) {
        return $.post(addProductFavoriteUrl, {
            productId: productId
        });
    };

    // 添加店铺收藏
    $.addStoreFavorite = function(storeId) {
        return $.post(addStoreFavoriteUrl, {
            storeId: storeId
        });
    };

    // 获取推广用户
    $.getSpreadUser = function() {
        return JSON.parse(localStorage.getItem(spreadUserLocalStorageKey));
    };

    // 设置推广用户
    $.setSpreadUser = function(spreadUser) {
        localStorage.setItem(spreadUserLocalStorageKey, JSON.stringify(spreadUser));
    };

    // 移除推广用户
    $.removeSpreadUser = function() {
        localStorage.removeItem(spreadUserLocalStorageKey);
    };

    // 生成推广Url
    $.generateSpreadUrl = function() {
        var url = arguments.length > 0 ? arguments[0] : location.href;
        var currentUser = $.getCurrentUser();

        if ($.base64 == null) {
            throw new Error("generateSpreadUrl requires jquery.base64.js");
        }
        return currentUser != null ? url.replace(/(#.*)?$/, (url.indexOf("?") >= 0 ? "&" : "?") + "SPREAD_" + $.base64.encode(currentUser.username)) : null;
    }

    // 重定向
    $.redirect = function(redirectUrl) {
        if (redirectUrl.indexOf(redirectUrlPrefix) == 0) {
            location.href = redirectUrl;
        }
    };

    // 重定向登录页面
    $.redirectLogin = function(options) {
        var settings = $.extend({}, $.redirectLogin.defaults, options);
        var loginUrl = loginUrls[settings.loginType];

        if ($.trim(loginUrl) == "") {
            return;
        }
        if ($.trim(settings.redirectUrl) != "") {
            loginUrl += (loginUrl.indexOf("?") >= 0 ? "&" : "?") + redirectUrlParameterName + "=" + encodeURIComponent(settings.redirectUrl);
        }
        location.href = loginUrl;
    };

    $.redirectLogin.defaults = {
        loginType: "member",
        redirectUrl: null
    };

    // AjaxSubmit
    var _ajaxSubmit = $.fn.ajaxSubmit;
    $.fn.ajaxSubmit = function(options, data, dataType, onSuccess) {
        var settings = $.extend({}, $.fn.ajaxSubmit.defaults, options);

        return _ajaxSubmit.call(this, settings, data, dataType, onSuccess);
    };

    $.fn.ajaxSubmit.defaults = {
        successMessage: true,
        successRedirectUrl: function(redirectUrlParameterName, data, textStatus, xhr, $form) {
            return Url.queryString(redirectUrlParameterName);
        },
        beforeSubmit: function(arr, $form, options) {
            var beforeEvent = $.Event("before.shopxx.ajaxSubmit", arguments);

            $form.trigger(beforeEvent);
            if (beforeEvent.isDefaultPrevented()) {
                return false;
            }
            $form.find(":submit").addClass("btn-before-submit").prop("disabled", true);
        },
        success: function(data, textStatus, xhr, $form) {
            var successMessage = $.isFunction(this.successMessage) ? this.successMessage.call(this, redirectUrlParameterName, data, textStatus, xhr, $form) : this.successMessage;
            var successRedirectUrl = $.isFunction(this.successRedirectUrl) ? this.successRedirectUrl.call(this, redirectUrlParameterName, data, textStatus, xhr, $form) : this.successRedirectUrl;

            $form.trigger("succ" + "ess.sho" + "px" + "x.ajaxSubmit", arguments);
            if ($.bootstrapGrowl != null && successMessage) {
                var message = typeof successMessage === "string" ? successMessage : data.message;

                if ($.trim(message) != "") {
                    $.bootstrapGrowl(message, {
                        type: "success"
                    });
                    if ($.trim(successRedirectUrl) != "") {
                        setTimeout(function() {
                            $form.find(":submit").prop("disabled", false).removeClass("btn-before-submit");
                            $.redirect(successRedirectUrl);
                        }, 1000);
                        return;
                    }
                }
            }
            if ($.trim(successRedirectUrl) != "") {
                $form.find(":submit").prop("disabled", false).removeClass("btn-before-submit");
                $.redirect(successRedirectUrl);
            }
        },
        error: function(xhr, textStatus, error, $form) {
            $form.find(":submit").prop("disabled", false).removeClass("btn-before-submit");
            $form.trigger("error.shopxx.ajaxSubmit", arguments);
        },
        complete: function(xhr, textStatus, $form) {
            $form.trigger("complete.shopxx.ajaxSubmit", arguments);
        }
    };

    // AJAX全局设置
    $.ajaxSetup({
        traditional: true,
        statusCode: {
            400: function(xhr, textStatus, errorThrown) {
                var data = JSON.parse(xhr.responseText);
                var message = data.message != null ? data.message : "请求参数错误";

                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl(message, {
                        type: "danger"
                    });
                }
            },
            401: function(xhr, textStatus, errorThrown) {
                var data = JSON.parse(xhr.responseText);
                var message = data.message != null ? data.message : "请登录后继续操作";

                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl(message, {
                        type: "warning"
                    });
                }
                setTimeout(function() {
                    $.redirectLogin({
                        redirectUrl: Url.getLocation()
                    });
                }, 3000);
            },
            403: function(xhr, textStatus, errorThrown) {
                var data = JSON.parse(xhr.responseText);
                var message = data.message != null ? data.message : "禁止访问";

                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl(message, {
                        type: "danger"
                    });
                }
            },
            404: function(xhr, textStatus, errorThrown) {
                var data = JSON.parse(xhr.responseText);
                var message = data.message != null ? data.message : "资源未找到";

                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl(message, {
                        type: "danger"
                    });
                }
            },
            422: function(xhr, textStatus, errorThrown) {
                var data = JSON.parse(xhr.responseText);
                var message = data.message != null ? data.message : "请求无法处理";

                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl(message, {
                        type: "warning"
                    });
                }
            }
        }
    });

    // Cookie
    if ($.cookie != null) {
        $.extend($.cookie.defaults, {
            path: "\/"
        });
    }

    // 提示框
    if ($.bootstrapGrowl != null) {
        $.extend($.bootstrapGrowl.default_options, {
            width: "auto",
            offset: {
                from: "top",
                amount: 120
            },
            align: "center",
            allow_dismiss: false
        });
    }

    // 弹出框
    if (typeof(bootbox) != "undefined") {
        bootbox.setDefaults({
            locale: "zh_CN",
            title: "提示信息"
        });

        bootbox.addLocale("zh_CN", {
            OK: "确 定",
            CANCEL: "取 消",
            CONFIRM: "确 定"
        });
    }

    // 下拉菜单
    if ($.fn.selectpicker != null) {
        $.fn.selectpicker.defaults = $.extend($.fn.selectpicker.defaults, {
            noneSelectedText: "没有选中任何内容",
            noneResultsText: "没有找到匹配内容",
            countSelectedText: "选中{1}中的{0}项",
            maxOptionsText: "超出限制(最多选择{n}项)",
            selectAllText: "全选",
            deselectAllText: "不选",
            multipleSeparator: "、"
        });
    }

    // 下拉菜单搜索
    if ($.fn.ajaxSelectPicker != null) {
        $.extend($.fn.ajaxSelectPicker.defaults, {
            langCode: "zh_CN"
        });

        $.fn.ajaxSelectPicker.locale["zh_CN"] = {
            currentlySelected: "当前选择",
            emptyTitle: "选择或输入",
            errorText: "没有找到匹配内容",
            searchPlaceholder: "查找...",
            statusInitialized: "输入查找内容",
            statusNoResults: "没有找到任何结果",
            statusSearching: "正在查找..."
        }
    }

    // 选择框
    if ($.fn.checkboxX != null) {
        $.extend($.fn.checkboxX.defaults, {
            size: "xs",
            threeState: false,
            valueChecked: "true",
            valueUnchecked: "false"
        });
    }

    // 日期选择
    if ($.fn.datetimepicker != null) {
        $.extend($.fn.datetimepicker.defaults, {
            locale: moment.locale("zh_CN"),
            format: $(this).data("date-format") != null ? $(this).data("date-format") : "YYYY-MM-DD"
        });
    }

    // 文件上传
    if ($.fn.fileinput != null) {
        $.extend($.fn.fileinput.defaults, {
            language: "zh_CN"
        });

        $.fn.fileinputLocales["zh_CN"] = {
            fileSingle: "文件",
            filePlural: "个文件",
            browseLabel: "选择...",
            removeLabel: "移除",
            removeTitle: "清除选中文件",
            cancelLabel: "取消",
            cancelTitle: "取消正在上传的文件",
            uploadLabel: "上传",
            uploadTitle: "上传选中文件",
            msgNo: "没有",
            msgNoFilesSelected: "没有选中文件",
            msgCancelled: "取消",
            msgPlaceholder: "选择{files}...",
            msgZoomModalHeading: "预览",
            msgFileRequired: "必须选择一个文件",
            msgSizeTooSmall: "文件 {name} 不允许小于{minSize}KB",
            msgSizeTooLarge: "文件 {name} 不允许大于{maxSize}KB",
            msgFilesTooLess: "上传文件数不允许小于{n}",
            msgFilesTooMany: "上传文件数不允许大于{m}",
            msgFileNotFound: "文件不存在 {name}",
            msgFileSecured: "文件无法读取 {name}",
            msgFileNotReadable: "文件无法读取 {name}",
            msgFilePreviewAborted: "取消 {name} 预览",
            msgFilePreviewError: "读取 {name} 错误",
            msgInvalidFileName: "文件名错误 {name}",
            msgInvalidFileType: "文件类型错误 {name}",
            msgInvalidFileExtension: "文件类型错误 {name}",
            msgFileTypes: {
                image: "图片",
                html: "HTML",
                text: "text",
                video: "video",
                audio: "audio",
                flash: "flash",
                pdf: "PDF",
                object: "object"
            },
            msgUploadAborted: "文件上传被中止",
            msgUploadThreshold: "处理中...",
            msgUploadBegin: "初始化...",
            msgUploadEnd: "上传成功",
            msgUploadEmpty: "暂无可上传文件",
            msgUploadError: "上传文件错误",
            msgValidationError: "验证错误",
            msgLoading: "加载第 {index} 文件 共 {files}...",
            msgProgress: "加载第 {index} 文件 共 {files} - {name} - {percent}% 完成",
            msgSelected: "{n} {files} 选中",
            msgFoldersNotAllowed: "只支持拖拽文件！跳过 {n} 拖拽的文件夹",
            msgImageWidthSmall: "文件 {name} 宽度不允许小于{size}像素",
            msgImageHeightSmall: "文件 {name} 高度不允许小于{size}像素",
            msgImageWidthLarge: "文件 {name} 宽度不允许大于{size}像素",
            msgImageHeightLarge: "文件 {name} 高度不允许大于{size}像素",
            msgImageResizeError: "无法调整图片大小",
            msgImageResizeException: "调整图片大小错误 {errors}",
            msgAjaxError: "{operation} 失败",
            msgAjaxProgressError: "{operation} 失败",
            ajaxOperations: {
                deleteThumb: "删除",
                uploadThumb: "上传",
                uploadBatch: "上传",
                uploadExtra: "上传"
            },
            dropZoneTitle: "拖拽文件到这里",
            dropZoneClickTitle: "(或点击{files}按钮选择文件)",
            fileActionSettings: {
                removeTitle: "删除",
                uploadTitle: "上传",
                uploadRetryTitle: "重试",
                downloadTitle: "下载",
                zoomTitle: "查看",
                dragTitle: "移动 / 重置",
                indicatorNewTitle: "没有上传",
                indicatorSuccessTitle: "上传",
                indicatorErrorTitle: "上传错误",
                indicatorLoadingTitle: "上传..."
            },
            previewZoomButtonTitles: {
                prev: "上一个",
                next: "下一个",
                toggleheader: "缩放",
                fullscreen: "全屏",
                borderless: "无边界模式",
                close: "关闭"
            }
        };
    }

    // 文本编辑器
    if ($.summernote != null) {
        $.extend($.summernote.options, {
            lang: "zh_CN",
            minHeight: 300,
            dialogsInBody: true,
            dialogsFade: true,
            callbacks: {
                onImageUpload: function(files) {
                    var $element = $(this);
                    var $files = $(files);

                    $files.each(function() {
                        var file = this;
                        var formData = new FormData();

                        formData.append("fileType", "IMAGE");
                        formData.append("file", file);
                        $.ajax({
                            url: "/common/file/upload",
                            type: "POST",
                            data: formData,
                            dataType: "json",
                            contentType: false,
                            cache: false,
                            processData: false
                        }).done(function(data) {
                            $element.summernote("insertImage", data.url);
                        });
                    });
                }
            }
        });

        $.extend($.summernote.lang, {
            "zh_CN": {
                font: {
                    bold: "粗体",
                    italic: "斜体",
                    underline: "下划线",
                    clear: "清除格式",
                    height: "行高",
                    name: "字体",
                    strikethrough: "删除线",
                    subscript: "下标",
                    superscript: "上标",
                    size: "字号"
                },
                image: {
                    image: "图片",
                    insert: "插入图片",
                    resizeFull: "缩放至 100%",
                    resizeHalf: "缩放至 50%",
                    resizeQuarter: "缩放至 25%",
                    floatLeft: "靠左浮动",
                    floatRight: "靠右浮动",
                    floatNone: "取消浮动",
                    shapeRounded: "形状: 圆角",
                    shapeCircle: "形状: 圆",
                    shapeThumbnail: "形状: 缩略图",
                    shapeNone: "形状: 无",
                    dragImageHere: "将图片拖拽至此处",
                    dropImage: "图片拖拽",
                    selectFromFiles: "从本地上传",
                    maximumFileSize: "文件大小最大值",
                    maximumFileSizeError: "文件大小超出最大值",
                    url: "图片地址",
                    remove: "移除图片"
                },
                video: {
                    video: "视频",
                    videoLink: "视频链接",
                    insert: "插入视频",
                    url: "视频地址",
                    providers: "(优酷, Instagram, DailyMotion, Youtube等)"
                },
                link: {
                    link: "链接",
                    insert: "插入链接",
                    unlink: "去除链接",
                    edit: "编辑链接",
                    textToDisplay: "显示文本",
                    url: "链接地址",
                    openInNewWindow: "在新窗口打开"
                },
                table: {
                    table: "表格"
                },
                hr: {
                    insert: "水平线"
                },
                style: {
                    style: "样式",
                    p: "普通",
                    blockquote: "引用",
                    pre: "代码",
                    h1: "标题 1",
                    h2: "标题 2",
                    h3: "标题 3",
                    h4: "标题 4",
                    h5: "标题 5",
                    h6: "标题 6"
                },
                lists: {
                    unordered: "无序列表",
                    ordered: "有序列表"
                },
                options: {
                    help: "帮助",
                    fullscreen: "全屏",
                    codeview: "源代码"
                },
                paragraph: {
                    paragraph: "段落",
                    outdent: "减少缩进",
                    indent: "增加缩进",
                    left: "左对齐",
                    center: "居中对齐",
                    right: "右对齐",
                    justify: "两端对齐"
                },
                color: {
                    recent: "最近使用",
                    more: "更多",
                    background: "背景",
                    foreground: "前景",
                    transparent: "透明",
                    setTransparent: "透明",
                    reset: "重置",
                    resetToDefault: "默认"
                },
                shortcut: {
                    shortcuts: "快捷键",
                    close: "关闭",
                    textFormatting: "文本格式",
                    action: "动作",
                    paragraphFormatting: "段落格式",
                    documentStyle: "文档样式",
                    extraKeys: "额外按键"
                },
                help: {
                    insertParagraph: "插入段落",
                    undo: "撤销",
                    redo: "重做",
                    tab: "增加缩进",
                    untab: "减少缩进",
                    bold: "粗体",
                    italic: "斜体",
                    underline: "下划线",
                    strikethrough: "删除线",
                    removeFormat: "清除格式",
                    justifyLeft: "左对齐",
                    justifyCenter: "居中对齐",
                    justifyRight: "右对齐",
                    justifyFull: "两端对齐",
                    insertUnorderedList: "无序列表",
                    insertOrderedList: "有序列表",
                    outdent: "减少缩进",
                    indent: "增加缩进",
                    formatPara: "设置选中内容样式为 普通",
                    formatH1: "设置选中内容样式为 标题1",
                    formatH2: "设置选中内容样式为 标题2",
                    formatH3: "设置选中内容样式为 标题3",
                    formatH4: "设置选中内容样式为 标题4",
                    formatH5: "设置选中内容样式为 标题5",
                    formatH6: "设置选中内容样式为 标题6",
                    insertHorizontalRule: "插入水平线",
                    "linkDialog.show": "显示链接对话框"
                },
                history: {
                    undo: "撤销",
                    redo: "重做"
                },
                specialChar: {
                    specialChar: "特殊字符",
                    select: "选择特殊字符"
                }
            }
        });
    }

    // 评级
    if ($.fn.rating != null) {
        $.extend($.fn.rating.defaults, {
            language: "zh_CN",
            step: 1,
            showClear: false,
            showCaption: false,
            emptyStar: '<i class="iconfont icon-favor"></i>',
            filledStar: '<i class="iconfont icon-favorfill"></i>'
        });

        $.fn.ratingLocales["zh_CN"] = {
            defaultCaption: "{rating} 星",
            starCaptions: {
                1: "一星",
                2: "二星",
                3: "三星",
                4: "四星",
                5: "五星"
            },
            clearButtonTitle: "清除",
            clearCaption: "未评级"
        };
    }

    // 滚动固定
    if ($.fn.scrollToFixed != null) {
        $.extend($.ScrollToFixed.defaultOptions, {
            zIndex: 100,
            spacerClass: "scroll-to-fixed-spacer"
        });
    }

    // 滚动加载
    if (typeof(Scrollload) != "undefined") {
        Scrollload.setGlobalOptions({
            loadingHtml: '<div class="scrollload-loader"><span></span><span></span><span></span><span></span><span></span></div>',
            noMoreDataHtml: '<p class="scrollload-no-more-data">没有更多数据</p>',
            exceptionHtml: '<p class="scrollload-exception">加载数据异常</p>',
            notEnoughRefreshPortHtml: '<div class="scrollload-loader"><span></span><span></span><span></span><span></span><span></span></div>',
            overRefreshPortHtml: '<div class="scrollload-loader"><span></span><span></span><span></span><span></span><span></span></div>',
            refreshingHtml: '<div class="scrollload-loader"><span></span><span></span><span></span><span></span><span></span></div>'
        });
    }

    // 表单验证
    if ($.validator != null) {
        var _greaterThan = $.validator.methods.greaterThan;
        var _greaterThanEqual = $.validator.methods.greaterThanEqual;
        var _lessThan = $.validator.methods.lessThan;
        var _lessThanEqual = $.validator.methods.lessThanEqual;

        $.validator.methods.greaterThan = function(value, element, param) {
            return this.optional(element) || _greaterThan.call(this, Number(value), element, param);
        }
        $.validator.methods.greaterThanEqual = function(value, element, param) {
            return this.optional(element) || _greaterThanEqual.call(this, Number(value), element, param);
        }
        $.validator.methods.lessThan = function(value, element, param) {
            return this.optional(element) || _lessThan.call(this, Number(value), element, param);
        }
        $.validator.methods.lessThanEqual = function(value, element, param) {
            return this.optional(element) || _lessThanEqual.call(this, Number(value), element, param);
        }
        $.validator.addMethod("positive", function(value, element) {
            return this.optional(element) || value > 0;
        }, "A positive number please");

        $.validator.addMethod("negative", function(value, element) {
            return this.optional(element) || value < 0;
        }, "A negative number please");

        $.validator.addMethod("decimal", function(value, element, param) {
            return this.optional(element) || new RegExp("^-?\\d{1," + (param.integer != null ? param.integer : "") + "}" + (param.fraction != null ? (param.fraction > 0 ? "(\\.\\d{1," + param.fraction + "})?$" : "$") : "(\\.\\d+)?$")).test(value);
        }, "Numeric value out of bounds");

        $.validator.addMethod("username", function(value, element) {
            return this.optional(element) || /^[0-9a-zA-Z_\u4e00-\u9fa5]+$/.test(value);
        }, "Only Chinese characters, letters, numbers and underscore can be used");

        $.validator.addMethod("zipCode", function(value, element) {
            return this.optional(element) || /^\d{6}$/.test(value);
        }, "The zip code is invalid");

        $.validator.addMethod("mobile", function(value, element) {
            return this.optional(element) || /^1[3|4|5|7|8]\d{9}$/.test(value);
        }, "Please enter a valid mobile number");

        $.validator.addMethod("phone", function(value, element) {
            return this.optional(element) || /^\d{3,4}-?\d{7,9}$/.test(value);
        }, "Please enter a valid phone number");

        $.validator.addMethod("notAllNumber", function(value, element) {
            return this.optional(element) || /^.*[^\d].*$/.test(value);
        }, "All numbers are not allowed");

        $.validator.addMethod("equals", function(value, element, param) {
            if (typeof param === "object") {
                return this.optional(element) || (param.ignoreCase ? param.value.toLowerCase() === value.toLowerCase() : param.value === value);
            } else {
                return this.optional(element) || param === value;
            }
        }, "Please enter a value equals {0}.");

        $.validator.addMethod("notEquals", function(value, element, param) {
            return this.optional(element) || !$.validator.methods.equals.call(this, value, element, param);
        }, "Please enter a value not equals {0}.");

        $.validator.setDefaults({
            ignore: ".ignore, .note-editable",
            ignoreTitle: true,
            errorElement: "span",
            errorClass: "help-block",
            normalizer: function(value) {
                return $.trim(value);
            },
            highlight: function(element, errorClass, validClass) {
                $(element).closest("td, .form-group").addClass("has-warning");
            },
            unhighlight: function(element, errorClass, validClass) {
                $(element).closest("td, .form-group").removeClass("has-warning");
            },
            errorPlacement: function($error, $element) {
                var $formGroup = $element.closest("td, [class^='col-'], .form-group");
                if ($formGroup.length > 0) {
                    $error.appendTo($formGroup);
                } else {
                    $error.insertAfter($element);
                }
            },
            invalidHandler: function(event, validator) {
                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl("输入有误，请检查后重新提交", {
                        type: "warning"
                    });
                }
            },
            submitHandler: function(form) {
                var $form = $(form);

                if ($.fn.ajaxForm != null && $form.hasClass("ajax-form")) {
                    $form.ajaxSubmit();
                } else {
                    form.submit();
                }
            }
        });

        $.extend($.validator.messages, {
            required: "必填",
            remote: "输入错误",
            email: "E-mail格式错误",
            url: "URL格式错误",
            date: "日期格式错误",
            dateISO: "日期格式错误",
            number: "只允许输入数字",
            digits: "只允许输入零或正整数",
            equalTo: "两次输入不一致",
            maxlength: $.validator.format("长度不允许大于{0}"),
            minlength: $.validator.format("长度不允许小于{0}"),
            rangelength: $.validator.format("长度必须在{0}-{1}之间"),
            range: $.validator.format("必须在{0}-{1}之间"),
            max: $.validator.format("不允许大于{0}"),
            min: $.validator.format("不允许小于{0}"),
            step: $.validator.format("必须在{0}之间"),
            maxWords: $.validator.format("最大允许输入{0}个单词"),
            minWords: $.validator.format("最小允许输入{0}个单词"),
            rangeWords: $.validator.format("只允许输入{0}-{1}个单词"),
            accept: $.validator.format("文件类型错误"),
            alphanumeric: "只允许输入字母、数字和下划线",
            bankaccountNL: "银行帐号格式错误",
            bankorgiroaccountNL: "银行帐号格式错误",
            bic: "BIC格式错误",
            cifES: "CIF格式错误",
            cpfBR: "CPF格式错误",
            creditcard: "信用卡格式错误",
            creditcardtypes: "信用卡格式错误",
            currency: "货币格式错误",
            dateFA: "日期格式错误",
            dateITA: "日期格式错误",
            dateNL: "日期格式错误",
            extension: $.validator.format("文件扩展名错误"),
            giroaccountNL: "汇款帐号格式错误",
            greaterThan: "输入值要求大于最小值",
            greaterThanEqual: "输入值要求大于等于最小值",
            iban: "IBAN格式错误",
            integer: "只允许输入整数",
            ipv4: "IPv4格式错误",
            ipv6: "IPv6格式错误",
            lessThan: "输入值要求小于最大值",
            lessThanEqual: "输入值要求小于等于最大值",
            lettersonly: "只允许输入字母",
            letterswithbasicpunc: "只允许输入字母和标点符号",
            maxfiles: "请选择不超过{0}个文件",
            maxsize: "单个文件大小不能超过{0}字节",
            maxsizetotal: "总文件大小不能超过{0}字节",
            mobileNL: "手机格式错误",
            mobileUK: "手机格式错误",
            netmask: "子网掩码格式错误",
            nieES: "NIE格式错误",
            nifES: "NIF格式错误",
            nipPL: "NIP格式错误",
            notEqualTo: "两次输入不允许相同",
            nowhitespace: "不允许输入空白字符",
            pattern: "格式错误",
            phoneNL: "电话号码格式错误",
            phonesUK: "电话号码格式错误",
            phoneUK: "电话号码格式错误",
            phoneUS: "电话号码格式错误",
            postalcodeBR: "邮编格式错误",
            postalCodeCA: "邮编格式错误",
            postalcodeIT: "邮编格式错误",
            postalcodeNL: "邮编格式错误",
            postcodeUK: "邮编格式错误",
            require_from_group: $.validator.format("必须填写{0}内容"),
            skip_or_fill_minimum: $.validator.format("必须填写{0}内容"),
            stateUS: "格式错误",
            strippedminlength: $.validator.format("请输入至少{0}个字符"),
            time: "时间格式错误",
            time12h: "时间格式错误",
            url2: "URL格式错误",
            vinUS: "VIN格式错误",
            zipCode: "邮编格式错误",
            zipcodeUS: "邮编格式错误",
            ziprange: "邮编范围应在902xx-xxxx和905xx-xxxx之间",
            positive: "只允许输入正数",
            negative: "只允许输入负数",
            decimal: "数值超出了允许范围",
            username: "只允许包含中文、英文、数字、下划线",
            mobile: "手机格式错误",
            phone: "电话号码格式错误",
            notAllNumber: "不允许全部为数字",
            equals: "请输入与{0}相等的值",
            notEquals: "请输入与{0}不相等的值"
        });
    }

})(jQuery);

$().ready(function() {

    var $document = $(document);
    var $form = $("form");
    var $redirectUrl = $("a[data-redirect-url]");
    var redirectUrlParameterName = "redirectUrl";
    var userAgent = navigator.userAgent.toLowerCase();
    var userAgentMatch = /(chrome)[ \/]([\w.]+)/.exec(userAgent) || /(webkit)[ \/]([\w.]+)/.exec(userAgent) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(userAgent) || /(msie) ([\w.]+)/.exec(userAgent) || userAgent.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(userAgent) || [];
    var $countdown = $("[data-target='countdown']");

    // 倒计时
    if ($.fn.animateNumber != null) {
        $countdown.prop("number", 3).animateNumber({
            number: 0,
            numberStep: function(now, tween) {
                var $target = $(tween.elem);
                var rounded_now = Math.round(now);

                if (now === tween.end) {
                    window.location.href = "/"
                } else {
                    $target.text(rounded_now);
                }
            }
        }, 3000, "linear");
    }

    // 浏览器不支持错误
    if (userAgentMatch[1] != null) {
        var browser = userAgentMatch[1];
        var version = userAgentMatch[2];
        var majorVersion = /^\d+/i.exec(version);

        if (browser == "msie" && parseFloat(majorVersion) < 10) {
            $("html, body").css({
                width: "100%",
                height: "100%",
                overflow: "hidden"
            }).append('<iframe class="unsupported-browser-iframe" src="/common/error/unsupported_browser" frameborder="0" scrolling="no" allowtransparency="true"></iframe>');
        }
    }

    // 用户注册/登录
    $document.on("registered.shopxx.user loggedIn.shopxx.user", function(event, currentUser) {
        $.setCurrentUser(currentUser);
        $.removeCurrentCartQuantity();
    });

    // 用户注销
    $document.on("loggedOut.shopxx.user", function(event, currentUser) {
        $.removeCurrentUser();
        $.removeCurrentCartQuantity();
        $.removeSpreadUser();
    });

    // 设置当前购物车数量
    // if ($.getCurrentCartQuantity() == null) {
    //     $.getCurrentCart().done(function(data) {
    //         var quantity = data.quantity != null ? data.quantity : 0;
    //         var beforeEvent = $.Event("before.shopxx.setCurrentCartQuantity");
    //
    //         $document.trigger(beforeEvent);
    //         if (beforeEvent.isDefaultPrevented()) {
    //             return;
    //         }
    //
    //         $.setCurrentCartQuantity(quantity);
    //         $document.trigger("complete.shopxx.setCurrentCartQuantity", quantity);
    //     });
    // }

    // 加入/修改/移除/清空购物车
    $document.on("success.shopxx.addCart success.shopxx.modifyCart success.shopxx.removeCart success.shopxx.clearCart", function(event, data) {
        var quantity = data.quantity != null ? data.quantity : 0;
        var beforeEvent = $.Event("before.shopxx.setCurrentCartQuantity");

        $document.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        $.setCurrentCartQuantity(quantity);
        $document.trigger("complete.shopxx.setCurrentCartQuantity", quantity);
    });

    // CSRF令牌
    $document.ajaxSend(function(event, xhr, settings) {
        var type = settings.type != null ? settings.type : "GET";

        if (!/^(GET|HEAD|TRACE|OPTIONS)$/i.test(type) && !settings.crossDomain) {
            var csrfToken = $.cookie("csrfToken");
            if (csrfToken != null) {
                xhr.setRequestHeader("X-Csrf-Token", csrfToken);
            }
        }
    });

    // CSRF令牌
    $form.submit(function() {
        var $element = $(this);
        var $csrfToken = $element.find("input[name='csrfToken']");
        var method = $element.attr("method") != null ? $element.attr("method") : "GET";

        if (!/^(GET|HEAD|TRACE|OPTIONS)$/i.test(method) && $csrfToken.length <= 0) {
            var csrfToken = $.cookie("csrfToken");
            if (csrfToken != null) {
                $element.append('<input name="csrfToken" type="hidden" value="' + csrfToken + '">');
            }
        }
    });

    // AJAX表单
    if ($.fn.ajaxForm != null) {
        $("form.ajax-form").submit(function() {
            var $element = $(this);
            var validator = $element.data("validator");

            if (validator != null) {
                return;
            }

            $element.ajaxSubmit();
            return false;
        });
    }

    // 重定向URL
    $redirectUrl.click(function() {
        var $element = $(this);
        var href = $element.attr("href");
        var redirectUrl = $element.data("redirect-url");

        if ($.trim(redirectUrl) == "") {
            redirectUrl = Url.getLocation();
        }

        location.href = href + (href.indexOf("?") >= 0 ? "&" : "?") + redirectUrlParameterName + "=" + encodeURIComponent(redirectUrl);
        return false;
    });

    // 下拉菜单
    if ($.validator != null) {
        $("select.selectpicker").change(function() {
            var $element = $(this);
            var validator = $element.closest("form").validate();

            if (validator != null) {
                validator.element($element);
            }
        });
    }

    // 日期选择
    if ($.fn.datetimepicker != null) {
        $("[data-provide='datetimepicker']").datetimepicker();

        $("[data-provide='datetimerangepicker']").each(function() {
            var $element = $(this);
            var $startDateTimePicker = $element.find("input:text:eq(0)");
            var $endDateTimePicker = $element.find("input:text:eq(1)");

            $startDateTimePicker.datetimepicker({
                format: $element.data("date-format")
            }).on("dp.change", function(event) {
                $endDateTimePicker.data("DateTimePicker").minDate(event.date);
            });

            $endDateTimePicker.datetimepicker({
                format: $element.data("date-format"),
                useCurrent: false
            }).on("dp.change", function(event) {
                $startDateTimePicker.data("DateTimePicker").maxDate(event.date);
            });
        });
    }

    // 文件上传
    if ($.fn.fileinput != null) {
        $("[data-provide='fileinput']").each(function() {
            var $element = $(this);
            var fileType = $element.data("file-type");
            var showPreview = $element.data("show-preview");
            var allowedFileExtensions;

            switch(fileType) {
                case "MEDIA":
                    allowedFileExtensions = "".split(",");
                    break;
                case "FILE":
                    allowedFileExtensions = "".split(",");
                    break;
                default:
                    allowedFileExtensions = "".split(",");
            }

            var $file = $('<input name="file" type="file">').insertAfter($element).fileinput({
                uploadUrl: "/common/file/upload",
                uploadExtraData: {
                    fileType: fileType != null ? fileType : "IMAGE"
                },
                allowedFileExtensions: allowedFileExtensions,
                maxFileSize: 10 * 1024,
                maxFileCount: 1,
                autoReplace: true,
                showUpload: false,
                showRemove: false,
                showClose: false,
                showUploadedThumbs: false,
                dropZoneEnabled: false,
                initialPreview: $element.val(),
                initialPreviewAsData: true,
                initialPreviewShowDelete: false,
                showPreview: showPreview != null ? showPreview : true,
                previewClass: "single-file-preview",
                layoutTemplates: {
                    footer: '<div class="file-thumbnail-footer">{actions}</div>',
                    actions: '<div class="file-actions"><div class="file-footer-buttons">{upload} {download} {delete} {zoom} {other}</div>{drag}<div class="clearfix"></div></div>'
                },
                fileActionSettings: {
                    showUpload: false,
                    showRemove: false,
                    showDrag: false
                },
                removeFromPreviewOnError: true,
                showAjaxErrorDetails: false
            }).on("fileloaded", function(event, file, previewId, index, reader) {
                $(this).fileinput("upload");
            }).on("fileuploaded", function(event, data, previewId, index) {
                $element.val(data.response.url);
            }).on("filecleared fileerror fileuploaderror", function() {
                $element.val("");
            });

            $element.data("file", $file);
        });
    }

    // 文本编辑器
    if ($.fn.summernote != null) {
        $("[data-provide='editor']").summernote();
    }

    // 滚动固定
    if ($.fn.scrollToFixed != null) {
        $("[data-spy='scrollToFixed']").each(function() {
            var $element = $(this);

            $element.scrollToFixed($element.data());
        });
    }

    // 图片延迟加载
    if ($.fn.lazyload != null) {
        $("img.lazy-load").lazyload({
            threshold: 300,
            effect: "fadeIn"
        });
    }

});
